//实现async/await

function fn(nums){
    return new Promise(resolve => {
        setTimeout(()=>{
            resolve(nums * 2)
        }, 1000)
    })
}

function* gen(){
    const num1 = yield fn(1)
    const num2 = yield fn(num1)
    const num3 = yield fn(num2)
    return num3
}

function generatorToAsync(generatorFn){
    const gen = generatorFn.apply(this,arguments) // gen有可能传参数
    return function() {
        // 返回一个Promise
        return new Promise((resolve,reject)=>{

            function go(key, arg){
                let res
                try {
                    res = gen[key](arg) //这里有可能会执行返回reject状态的Promise
                } catch(error) {
                    reject(error) //报错的话会走catch，直接reject
                }

                const { value, done } = res //解构获得value和done
                if(done){
                    // 如果done为true，说明走完了，进行resolve(value)
                    resolve(value)
                } else {
                    // 如果done为false，说明没走完，还得继续走
                    // value有可能是：常量，Promise，Promise有可能是成功或者失败
                    Promise.resolve(value).then(val=>go('next',val), err => go('throw', err))
                }
            }

            go('next') // 第一次执行
        })
    }
}

const asyncFn = generatorToAsync(gen)
asyncFn().then(res=>console.log(res))

//async/await的效果
async function asyncFn1(){
    const num1 = await fn(1)
    const num2 = await fn(num1)
    const num3 = await fn(num2)
    return num3 
}
asyncFn1().then(res=>console.log(res))

/**
 * 总结：
 * 1、await只能在async函数中使用，不然会报错
 * 2、async函数返回的是一个(状态为fulfilled)Promise对象，有无值看有无return值，无return值则返回undefined
 * 3、await后面最好是接Promise，虽然接其他值也能达到排队效果
 * 4、async/await作用是用同步方式，执行异步操作
 */

/**
 * async await 由Generator 函数和 Promise 共同完成
 * Generator函数又会返回一个 Iterator接口
 * 
 * Iterator
 * 迭代器（Iterator）是一种接口，或者说是一种机制。它能为各种不同的数据结构提供统一的访问机制，
 * 任何数据结构只要部署 Iterator 接口，就可以完成遍历操作（即依次处理该数据结构的所有成员）。
 * 主要作用是为了为各种数据结构提供一个统一的简便的访问接口，使数据结构的成员能够按某种次数排列。
 * Iterator本质上是一个指针对象
 * 原生数据结构中的Iterator，在js中，有一些内置的数据结构天生就具有Iterator接口，包括：
 * Array、Map、Set、String、函数的arguments对象、NodeList对象
 * 要获取到这些数据结构中的Iterator接口，需调用Symbol.iterator方法：
 */

const arr = ['a', 'b', 'c']
const iter = arr[Symbol.iterator]()
console.log(iter.next()) // { value: 'a', done: false }
console.log(iter.next()) // { value: 'b', done: false }
console.log(iter.next()) // { value: 'c', done: true }
console.log(iter.next()) // { value: undefined, done: true }
//其中value是每次遍历到的值，done代表是否将该数组遍历完全

/**
 * 自定义的 Iterator
 * 正如前面所说，Iterator 只是为了给我们提供一个统一的访问接口，所以任何一个普通的对象都是可以实现 Iterator 的，
 * 只需要定义Symbol.iterator方法就可以了
 */
const iterObj = {
    value: 0,
    [Symbol.iterator]() {
        const self = this
        return {
            next (){
                const value = self.value++
                const done = value > 2
                return {
                    value: done ?  undefined : value,
                    done
                }
            }
        }
    }
}
const itera = iterObj[Symbol.iterator]()
console.log(itera.next()) // { value: 0, done: false }
console.log(itera.next()) // { value: 1, done: false }
console.log(itera.next()) // { value: 2, done: false }
console.log(itera.next()) // { value: undefined, done: true }
console.log(itera.next()) // { value: undefined, done: true }
/**
 * 我们实现的 Iterator 接口需要有next方法（这个方法会在类型for...of这样的语句中会被调用），
 * 同时在next方法中要对完成状态和未完成的状态做判断，当全部迭代完毕后应该把done设置为true，
 * 同时返回的value应该为undefined
 */